package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.constants.article.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
@Transactional
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    ApArticleMapper apArticleMapper;

    @Override
    public void computeHotArticle() {
        //1 查询前5天的 （已上架、未删除） 文章数据
        String date = LocalDateTime.now().minusDays(5)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<ApArticle> articleList = apArticleMapper.selectArticleByDate(date);
        //2 计算热点文章分值
        List<HotArticleVo> hotArticleVoList = computeArticleScore(articleList);
        //3 获取每一个频道缓存热点较高的30条文章
        cacheTagToRedis(hotArticleVoList);
    }

    /**
     * 重新计算文章热度  更新redis缓存
     * @param aggBehavior
     */
    @Override
    public void updateApArticle(AggBehaviorDTO aggBehavior) {
        ApArticle apArticle = apArticleMapper.selectById(aggBehavior.getArticleId());
        if (apArticle == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        int likes = (int) (apArticle.getLikes()== null ? 0: apArticle.getLikes() + aggBehavior.getLike());
        apArticle.setLikes(likes);
        int views = (int) (apArticle.getViews()== null ? 0: apArticle.getViews() + aggBehavior.getView());
        apArticle.setViews(views);
        int comments = (int) (apArticle.getComment()== null ? 0: apArticle.getComment() + aggBehavior.getComment());
        apArticle.setComment(comments);
        int collentions = (int) (apArticle.getCollection()== null ? 0: apArticle.getCollection() + aggBehavior.getCollect());
        apArticle.setCollection(collentions);
        apArticleMapper.updateById(apArticle);
        Integer score = computeScore(apArticle);
        String nowStr = DateUtils.dateToString(new Date());
        String publishStr = DateUtils.dateToString(apArticle.getPublishTime());
        if (nowStr.equals(publishStr)) {
            score = score *3;
        }
        //基于当前文章 替换该频道热度较低的文章
        updateArticleCache(score,apArticle,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        //基于当前文章 替换推荐频道热度较低的文章
        updateArticleCache(score,apArticle,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     * 基于当前文章 替换该频道热度较低的文章
     * @param score
     * @param apArticle
     * @param cacheKey
     */
    private void updateArticleCache(Integer score, ApArticle apArticle, String cacheKey) {
        String jsonHotArticle = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isBlank(jsonHotArticle)) {
            return;
        }
        List<HotArticleVo> hotArticleVos = JSON.parseArray(jsonHotArticle, HotArticleVo.class);
        boolean flag =false;
        for (HotArticleVo hotArticleVo : hotArticleVos) {
            if (hotArticleVo.getId().equals(apArticle.getId())) {
                hotArticleVo.setScore(score);
                flag=true;
                break;
            }
        }
        if (!flag){
            hotArticleVos.add(parseArticleVo(apArticle));
        }

        hotArticleVos = hotArticleVos.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30).collect(Collectors.toList());

        redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(hotArticleVos));
    }

    private HotArticleVo parseArticleVo(ApArticle apArticle){
        HotArticleVo articleVo = new HotArticleVo();
        BeanUtils.copyProperties(apArticle,articleVo);
        // 计算文章得分
        Integer score = computeScore(apArticle);
        articleVo.setScore(score);
        return articleVo;
    }
    @Autowired
    AdminFeign adminFeign;

    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * 获取每一个频道缓存热点较高的30条文章
     * @param hotArticleVoList
     */
    private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
        ResponseResult<List<AdChannel>> result = adminFeign.findAll();
        if (!result.checkCode()) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,"远程调用adminFeign失败");
        }
        List<AdChannel> channelList = result.getData();
        for (AdChannel adChannel : channelList) {
            List<HotArticleVo> hotArticleVos = hotArticleVoList.stream().filter(hotArticleVo -> hotArticleVo.getChannelId().equals(adChannel.getId()))
                    .collect(Collectors.toList());
            sortAndCache(hotArticleVos, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
        }
        sortAndCache(hotArticleVoList,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);
    }

    /**
     * 给推荐频道缓存30条数据  所有文章排序之后的前30条
     * @param hotArticleVos
     * @param cacheKey
     */
    private void sortAndCache(List<HotArticleVo> hotArticleVos, String cacheKey) {
        hotArticleVos = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleVos));
    }


    /**
     * 计算热点文章分值
     * @param articleList
     * @return
     */
    private List<HotArticleVo> computeArticleScore(List<ApArticle> articleList) {
        return articleList.stream().map(apArticle -> {
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle,hotArticleVo);
                    Integer score = computeScore(apArticle);
                    hotArticleVo.setScore(score);
                    return hotArticleVo;
                }).collect(Collectors.toList());
    }

    /**
     * 计算文章分值算法
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}
